from django import template
from django import forms
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import simplejson
from pirate_comments.models import Comment
from django.db import transaction
from django.middleware import csrf
from django.contrib.contenttypes.models import ContentType
from pirate_profile.models import Profile
from django.utils.encoding import smart_str
from pirate_core.helpers import clean_html
from pirate_core.views import template_for_model

import datetime
from pirate_signals.models import notification_send, relationship_event

from django.shortcuts import get_object_or_404

from pirate_core.views import HttpRedirectException, namespace_get


from customtags.decorators import block_decorator
register = template.Library()
block = block_decorator(register)

get_namespace = namespace_get('pp_comment')


@block
def pp_comment_count(context, nodelist, *args, **kwargs): 
    context.push()
    namespace = get_namespace(context)

    object_pk = kwargs.get('object', None)
    
    comments = Comment.objects.all()
    count = len(list(comments.filter(object_pk=object_pk)))
    
    namespace['count'] = count
    
    output = nodelist.render(context)
    context.pop()

    return output

@block
def pp_comment_get(context, nodelist, *args, **kwargs): 
    pass

@block
def pp_comment_list_get(context, nodelist, *args, **kwargs): 

    """we have to render the tree html here, because recursive includes are not allowed in django templates
    this could be more efficient with pre/post order tree traversal, but for now this suffices.
    mptt and treebeard both are not designed for GAE, need a tree traversal library for non-rel"""
    
    context.push()
    namespace = get_namespace(context)

    object_pk = kwargs.get('object', None)
    user = kwargs.get('user',None) #needs request.user for reply submission
    if object_pk is None:
        raise ValueError("pp_consensus_get tag requires that a consensus object be passed "
                             "to it assigned to the 'object=' argument, and that the str "
                             "be assigned the string value 'consensus.")

    request = kwargs.get('request',None)
    
    comment_tree = []
    
    comments = Comment.objects.all()
    comments = comments.filter(object_pk=object_pk,is_root=True)
    comments = comments.order_by('-submit_date')
    
    for c in comments:
        if c.is_leaf:
            comment_tree.append((c,0))
        else:
            comment_tree.append(get_children(object_pk,c))
            
    tree_html = render_to_comment_tree_html(comment_tree, user, request)
    tree_html = "<ul class='collapsible_comments'>" + tree_html + "</ul>"
    namespace['comments'] = tree_html
    namespace['debug_comments'] = comment_tree
    
    output = nodelist.render(context)
    context.pop()

    return output

def render_to_comment_tree_html(comment_tree, user, request):
    """Comment tree in form:
        c_tree = [[Comment1, [Comment1_2, Comment1_3, Comment1_4]], Comment2, Comment 3]
    must be rendered as a <ul>...<li><ul> ... <li>render_comment()</li> ... </ul> </li> </ul>"""
    
    ret_html = ""
    for comment in comment_tree:
        if isinstance(comment, tuple):
            ret_html+="<li> "+ render_comment(comment[0], comment[1], user, request) + "</li>"
        elif isinstance(comment, list):
            ret_html+= "<li> "+ render_comment(comment[0][0], comment[0][1], user, request) + "<ul>" + render_to_comment_tree_html(comment[1], user, request) + "</ul></li>"

    return ret_html

#TODO: FIX ME
def generate_time_string(then, now):
    time_to = abs(now - then)
    hours = time_to.seconds / 360
    
    if hours > 24:
        ret = str(time_to.days) + " days ago"
    elif hours == 0:
        if time_to.seconds / 60 == 0:
            ret = str(time_to.seconds) + " seconds ago"
        else:
            ret = str(time_to.seconds / 60) + " minutes ago"
        
    else:
        ret = str(time_to.seconds / 360 ) + " hours ago"
    return " said " + ret


#ok this is as ugly as it gets, but there's little other ways to generate this html that I am aware of
def render_comment(comment_obj, count, user, request):
    content_type = ContentType.objects.get_for_model(comment_obj.user)
    path = template_for_model(str(comment_obj.content_type)) + "?_t=" + str(comment_obj.content_type.pk) + "&_o=" + str(comment_obj.object_pk)  +"&_c=comment" + str(comment_obj.id)
    form = ReplyForm(initial={'is_root':False,'is_leaf':True,'content_type':comment_obj.content_type,'object_pk':comment_obj.object_pk,'reply_to':comment_obj, 'submit_date':datetime.datetime.now(),'user':user})
    if count == 0 or count > 1: plural = "s"
    else: plural = ""
    """returns the relevant html for a single atomic comment given the comment object"""
    return "<div class='comment" + str(comment_obj.id) + "'> <div class='comment_user'> <a href='/user_profile.html" + "?_t=" + str(content_type.pk) + "&_o=" + str(comment_obj.user.pk) + "'>" + str(comment_obj.user.username)+ "</a>" + generate_time_string(comment_obj.submit_date, datetime.datetime.now()) + ":</div><div>" + smart_str(comment_obj.text, encoding='utf-8', strings_only=False, errors='strict') +"</div><div class='comment_reply'>" + " <a href='javascript:;' onmousedown=" + "'toggleSlide(" + '"add_reply' + str(comment_obj.id) + '"' + ");'>reply</a> <a href='/" + path + "'>permalink</a><div id='add_reply" + str(comment_obj.id) + "' style='display:none; overflow:hidden; height:250px;'><form id='add_reply_form" + str(comment_obj.id) +"' method='post' action=''><div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='" + str(csrf.get_token(request)) + "' /><input id='reply_to_object' type='hidden' name='reply_to_object' value='" + str(comment_obj.id)+ "'/></div>" + str(form.as_p()) + "<input type='submit' class='button green' value='Submit Comment'></form></div></div></div>"

def get_children(object_pk,cur_comment):
    get_list = []
    comments = Comment.objects.all()
    comments = comments.filter(object_pk=object_pk,is_root=False,reply_to=cur_comment)
    for c in comments:
        if c.is_leaf:
            get_list.append((c,0))
        else:
            get_list.append(get_children(object_pk,c))
    return [(cur_comment, len(comments)), get_list]


@block
def pp_comment_form(context, nodelist, *args, **kwargs):
    '''
    This block tag can create or process forms either to create or to modify issues.
    Usage is as follows:

    {% pp_comment_form POST=request.POST object=request.object user=request.user %}
       Do stuff with {{ pp-comment.form }}.
    {% endpp_comment_form %}
    '''

    context.push()
    namespace = get_namespace(context)

    POST  = kwargs.get('POST', None)
    reply_to = kwargs.get('object',None)
    user = kwargs.get('user', None)
    comment = kwargs.get('edit',None)

    if isinstance(reply_to, Comment):
        c_type = reply_to.content_type
        object_pk = reply_to.object_pk
    else:
        c_type = ContentType.objects.get_for_model(reply_to.__class__)
        object_pk = reply_to.id
        reply_to = None
        
    if POST and POST.get("form_id") == "pp_comment_form":
        form = CommentForm(POST) if comment is None else CommentForm(POST, instance=comment)
        if form.is_valid():
            newcomment = form.save(commit=False)
            newcomment.user = user
            newcomment.content_type = c_type
            newcomment.object_pk = object_pk
            newcomment.text = clean_html(newcomment.text)
            newcomment.reply_to = reply_to
            newcomment.is_leaf = True
            newcomment.submit_date = datetime.datetime.now()
            if reply_to == None: newcomment.is_root = True #non-root comments will use auto-generated reply form
            else: newcomment.is_root = False
            newcomment.save()
            if comment is None: #if comment is new and not editted
                notification_send.send(sender=newcomment,obj=newcomment,reply_to=newcomment.content_object)
                relationship_event.send(sender=newcomment,obj=newcomment,parent=newcomment.content_object)
        raise HttpRedirectException(HttpResponseRedirect(newcomment.get_absolute_url()))
    
    if POST and POST.get("form_id") == "pp_reply_form":
        form = ReplyForm(POST) if comment is None else ReplyForm(POST, instance=comment)
        if form.is_valid():
            com_rep = int(POST.get("reply_to_object"))
            newcomment = form.save(commit=False)
            newcomment.user = user
            newcomment.content_type = c_type
            newcomment.object_pk = object_pk
            newcomment.reply_to = Comment.objects.get(pk=com_rep)
            newcomment.reply_to.is_leaf = False
            newcomment.reply_to.save()
            newcomment.text = clean_html(newcomment.text)
            newcomment.is_leaf = True
            newcomment.is_root = False
            newcomment.submit_date = datetime.datetime.now()
            newcomment.save()
            if comment is None: #if comment is new and not editted
                notification_send.send(sender=newcomment,obj=newcomment,reply_to=newcomment.reply_to)
        raise HttpRedirectException(HttpResponseRedirect(newcomment.get_absolute_url()))
        
    else: form = CommentForm() if comment is None else CommentForm(instance=comment)

            
    namespace['form'] = form
    output = nodelist.render(context)
    context.pop()

    return output

class CommentForm(forms.ModelForm):
    '''
    This form is used to allow creation and modification of comment objects.  
    It extends FormMixin in order to provide a create() class method, which
    is used to process POST, path, and object variables in a consistant way,
    and in order to automatically provide the form with a form_id.
    '''

    def save(self, commit=True):
        new_comment = super(CommentForm, self).save(commit=commit)
        return new_comment

    class Meta:
        model = Comment
        exclude = ('user','object_pk','content_type','reply_to','submit_date', 'is_leaf','is_root', 'content_object')
    #need to grab user from authenticatio
    form_id = forms.CharField(widget=forms.HiddenInput(), initial="pp_comment_form")
    text = forms.CharField(widget=forms.Textarea)
    
class ReplyForm(forms.ModelForm):
    '''
    This form is used to allow creation and modification of comment objects.  
    It extends FormMixin in order to provide a create() class method, which
    is used to process POST, path, and object variables in a consistant way,
    and in order to automatically provide the form with a form_id.
    '''

    def save(self, commit=True):
        new_comment = super(ReplyForm, self).save(commit=commit)
        return new_comment

    class Meta:
        model = Comment
        exclude = ('user','object_pk','content_type','reply_to','submit_date', 'is_leaf','is_root', 'content_object')
    #need to grab user from authenticatio
    form_id = forms.CharField(widget=forms.HiddenInput(), initial="pp_reply_form")
    text = forms.CharField(widget=forms.Textarea,label="")


